#include <stdio.h>
#include <stdlib.h>

#include "FAT32.h"

#include "fatdefs.h"

#include "GLOBAL.h"
#include <string.h>

// End of cluster chain return value
#define FS_LAST_CHAIN 0x0FFFFFFF

// Used to fill data for metadata-less mp3 files as well as songs whose 
// metadata contains empty strings
const char unknownArtist[] = "Unknown Artist";
const char unknownAlbum[] =  "Unknown Album";

// Variables created by original code that we will reference
extern unsigned char   buffer[MAX_SEC_PER_CLS*BYTE_PER_SECTOR];
extern unsigned long   firstDataSector, rootCluster, totalClusters;
extern unsigned short  sectorPerCluster, reservedSectorCount;
extern unsigned long   unusedSectors;
extern unsigned char   freeClusterCountUpdated;

/**************************************************************************
* Purpose:	Determines how many mp3 files there are on sd card partition 1
* Input:	No input
* Output:	Returns the number of songs found that have extension "mp3"
***************************************************************************/
unsigned long getMp3Count ()
{
	unsigned long cluster, firstSector, nextCluster;
	struct dir_Structure *dir;
	unsigned short i, sector;
	unsigned long count = 0;

	cluster = rootCluster;		// root cluster

	while (1)
	{
		firstSector = getFirstSector (cluster);

		for (sector = 0; sector < sectorPerCluster; sector++)
		{
			CardRead (firstSector + sector, 1, buffer);		

			for (i = 0; i < BYTE_PER_SECTOR; i += 32)	// go thru each directory structure
			{
				if (buffer[i] == 0x00)
				{
					//printf("Reached end of directory\n");
					return count;
				}

				else if (buffer[i] != 0xE5)
				{
					dir = (struct dir_Structure*) &buffer[i];
					
					// If we are at file entry
					if ( ( dir->attrib & 0x1E) == 0 )
					{
						if (memcmp(dir->name+8,"MP3",3) == 0)
						{
							count++;
						}
					}
				}
			}
		}

		nextCluster = getSetNextCluster (cluster, GET, 0);

		if (nextCluster > 0x0ffffff6)
		{
			return count;			// End of Cluster Chain
		}
	
		if (nextCluster == 0) 
			return count;			// Error in getting cluster
   
		cluster = nextCluster;
	}
 
	return count;
}

/**************************************************************************
* Purpose:	Finds the next mp3 file and opens a fileptr to it based on the 
*			prevous song number
* Input: Previous song number (-1 indicates first song...ie song 0)
* Output: Returns a fileptr to song or a null if song dne
***************************************************************************/
struct FAT_FILE_t * getNextMp3File(long long prev)
{
	unsigned long cluster, firstSector, nextCluster;
	struct dir_Structure *dir;
	unsigned short i, sector;
	long long count = -1;
	struct FAT_FILE_t * fp = NULL;

	fp = (struct FAT_FILE_t*)malloc(sizeof(struct FAT_FILE_t));

	cluster = rootCluster;		// root cluster

	while (1)
	{
		firstSector = getFirstSector (cluster);

		for (sector = 0; sector < sectorPerCluster; sector++)
		{
			CardRead (firstSector + sector, 1, buffer);		

			for (i = 0; i < BYTE_PER_SECTOR; i += 32)	// go thru each directory structure
			{
				if (buffer[i] == 0x00)
				{
					//printf("Reached end of directory\n");
					free(fp);
					return NULL;
				}

				else if (buffer[i] != 0xE5)
				{
					dir = (struct dir_Structure*) &buffer[i];
					
					// If we are at file entry
					if ( ( dir->attrib & 0x1E) == 0 )
					{
						if(memcmp(dir->name+8,"MP3",3)==0)
						{
							if (++count == prev)
							{
								fp->start_cluster = fp->current_cluster = (((unsigned long)(dir->firstClusterHI)<<16) | (unsigned long)dir->firstClusterLO) & 0x0FFFFFFF;
								fp->size = dir->fileSize;
								memcpy(fp->filename,dir->name,11);
								fp->filename[11] = '\0';
								fp->flags = FAT_FILE_GOOD;
								CardRead( getFirstSector(fp->start_cluster) ,1,fp->buf);
								return fp;
							}
						}

					}
				}
			}
		}

		nextCluster = getSetNextCluster (cluster, GET, 0);

		if (nextCluster > 0x0ffffff6)
		{
			free(fp);
			return NULL;			// End of Cluster Chain
		}
	
		if (nextCluster == 0) 
		{
			free(fp);
			return NULL;			// Error in getting cluster
		}
   
		cluster = nextCluster;
	}
	
	free(fp);
	return NULL;
}

/**************************************************************************
* Purpose:	Populates a songdata structure with metadata(if available)
*			from file in fileptr array
* Input:	Array of songdata structure pointers, array of pointers to 
*			fileptrs and the current mp3 file number to populate data from
* Output:	No output...but does load songdata structure in array with data
***************************************************************************/
void getSongData(struct SongData ** song, struct FAT_FILE_t **fpArray, int index)
{
	unsigned char buf[128];

	fat_fseek(fpArray[index],128,1);

	memcpy(song[index]->filename, fpArray[index]->filename, 12);
	
	fat_fread(buf,128,fpArray[index]);

	if (strncmp((char*)buf,"TAG",3) == 0)
	{
		// Copy data		
		strncpy((char*)song[index]->title, (char*)(buf+3), 30);
		strncpy((char*)song[index]->artist,(char*)(buf+3 + 30), 30);
		strncpy((char*)song[index]->album,(char*)(buf+3+60),30);
		song[index]->title[29] = '\0';
		song[index]->artist[29] = '\0';
		song[index]->album[29] = '\0';
	}
	else
	{
		memcpy(song[index]->title,fpArray[index]->filename,12);
		memcpy(song[index]->artist,unknownArtist, sizeof(unknownArtist));
		memcpy(song[index]->album,unknownAlbum, sizeof(unknownAlbum));
		
		song[index]->title[11] = '\0';
		song[index]->artist[14] = '\0';
		song[index]->album[13] = '\0';
	}
	fat_fseek(fpArray[index],0,0);
	return;
}

/****************************************************************************
* Purpose:	Reads up to "num_bytes" from fileptr "fp" and places data in
*			"buf"
* Input:	A destination buffer "buf", the number of bytes to read
*			"num_bytes" and the fileptr "fp"
* Output:   Returns the number of bytes read
****************************************************************************/
int	fat_fread(void * buf, unsigned int num_bytes, struct FAT_FILE_t * fp)
{
	unsigned int i;

	if (buf == NULL || fp == NULL)
	{
		printf("Invalid filePtr, buf pointer or zero read byte count\r\n");
		return 0;
	}

	if ( FAT_FILE_GOOD != (fp->flags) )
	{
		// If the file is at EOF or is in fail state don't attempt to read
		printf("File Flag is either EOF or ERR\r\n");
		return 0;
	}

	// There is a request for more bytes then there are left in file only allow set amount to read to size - byte_index
	if ( num_bytes > ( fp->size - fp->byte_index ) )
		num_bytes = (unsigned short)(fp->size - fp->byte_index);//actual_num_to_read = fp->size - fp->byte_index;

	for ( i = 0; i < num_bytes; i++)
	{
		//printf("Byte index: %u, byte value: %u\r\n", fp->byte_index, fp->buf[ ( fp->byte_index % (uint32_t)512 ) ]);

		( (unsigned char*) buf )[i] = fp->buf[ ( fp->byte_index % 512 ) ];

		fp->byte_index++;

		if ( (fp->byte_index % 512) == 0 )
		{
			fat_file_buffer_next_sector(fp);

			if (fp->flags != FAT_FILE_GOOD)
				break;
		}

	}

	if (fp->size == fp->byte_index)
		fp->flags = FAT_FILE_EOF;

	return i;
}

/****************************************************************************
* Purpose:	Buffers in the next sector in file
* Input:	Valid fileptr
* Output:   No Output
****************************************************************************/
void fat_file_buffer_next_sector(struct FAT_FILE_t * fp)
{
	if (!fp || fp->flags != FAT_FILE_GOOD)
	{
		return;
	}

	if ( (fp->sector_index) < (sectorPerCluster - 1) )
		fp->sector_index++;

	else
	{
		//temp = fat_find_next_cluster( (uint32_t) fp->current_cluster );

		//printf("Current Cluster: %lu, ",fp->current_cluster);
		fp->current_cluster = getSetNextCluster (fp->current_cluster, GET, 0);
		//printf("New Cluster: %lu\r\n",fp->current_cluster);

		// If there are no more sectors to read -> something went wrong
		if (fp->current_cluster == FS_LAST_CHAIN)
		{
			//file->current_cluster = temp;
			printf("At last cluster in chain\r\n");
			fp->flags = FAT_FILE_EOF;
			return;
		}

		// Copy new valid cluster value into current_cluster, increment our index, and reset sector_index
		//fp->current_cluster = (uint32_t) temp;
		fp->cluster_index++;
		fp->sector_index = 0;
	}

	// Buffer in first sector of new cluster

	/*if(!fs.fat_io.read_sector(fs.loc.data_start_sector + fp->sector_index + (fp->current_cluster - 2) * fs.loc.sectors_per_cluster , (uint8_t*) fp->buf ) )
	{
		// If we fail to buffer file set file to fail state
		printf("Failed during sector buffer in fat_buf_next_clu()\r\n");
		fp->flags = FAT_FILE_ERR;
		return;
	}*/

	CardRead(getFirstSector(fp->current_cluster)+fp->sector_index,1,(unsigned char*)fp->buf);

	return;
}

/****************************************************************************
* Purpose:	Seeks to Beginning or End minus offset (not a true implementation
*			of fseek)
* Input:	Valid Fileptr, offset value (if seeking to end), and which end to
*			seek to (1 = END, 0 = BEGIN)
* Output:   Returns 0 on success, non-zero on failure
****************************************************************************/
int fat_fseek(struct FAT_FILE_t *fp, unsigned long offset, unsigned char pos)
{
	unsigned long desired_loc;
	unsigned int cluster2read;
	unsigned long sectors2read;
	unsigned long i;

	if (!fp)
		return -1;

	if (fp->flags == FAT_FILE_ERR)
		return -1;

	if (pos == 1)
	{
		fp->current_cluster = fp->start_cluster;
		fp->byte_index = fp->cluster_index = fp->sector_index = 0;
		fp->flags = FAT_FILE_GOOD;

		desired_loc = fp->size - offset;

		sectors2read = desired_loc / 512;
		cluster2read = sectors2read / sectorPerCluster;

		for (i = 0; i < cluster2read; i++)
		{
			fp->current_cluster = getSetNextCluster(fp->current_cluster,GET,0);
			fp->cluster_index++;
		}

		fp->sector_index = sectors2read % sectorPerCluster;

		/*if (!fs.fat_io.read_sector(fs.loc.data_start_sector + ( (fp->current_cluster - 2) * (fs.loc.sectors_per_cluster) + fp->sector_index), (unsigned char*)fp->buf) )
		{
			printf("Error reading in buffer, sector val = %lu\r\n", fs.loc.data_start_sector + ( (fp->start_cluster - 2) * fs.loc.sectors_per_cluster ) );
			return -1;
		}*/

		CardRead(getFirstSector(fp->current_cluster) + fp->sector_index,1,(unsigned char*)fp->buf);

		fp->byte_index = desired_loc;

		//printf("Bytes remaining: %lu\r\n",fp->size - fp->byte_index);
		return 0;
	}
	else if (pos == 0)
	{
		fp->current_cluster = fp->start_cluster;
		fp->byte_index = fp->cluster_index = fp->sector_index = 0;
		fp->flags = FAT_FILE_GOOD;

		CardRead(getFirstSector(fp->start_cluster),1,(unsigned char*)fp->buf);

		fp->byte_index = 0;
		return 0;
	}
	return -1;
}

